package com.kyub.spring.gears.web;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.multipart.support.ByteArrayMultipartFileEditor;
import org.springframework.beans.propertyeditors.CustomDateEditor;

import java.beans.PropertyEditorSupport;
import java.util.*;
import java.text.SimpleDateFormat;

import com.kyub.spring.gears.model.PojoDescriptor;
import com.kyub.spring.gears.model.PojoRegistry;
import com.kyub.spring.gears.model.PojoRegistryEntry;
import com.kyub.spring.gears.introspect.PojoDescriptorFactory;
import com.kyub.spring.gears.PrjLogger;
import com.kyub.spring.gears.vo.NavigableCollection;
import com.kyub.spring.gears.services.DaoService;
import com.kyub.spring.gears.services.ControllerDelegateService;

import javax.naming.ConfigurationException;

/**
 */
public abstract class ControllerAncestor {

    protected static final Map<Class, PropertyEditorSupport> pecache = new HashMap<Class, PropertyEditorSupport>();
    protected static final Map<String, PojoDescriptor> pojoDescriptions = new HashMap<String, PojoDescriptor>();

    private PojoDescriptorFactory factory;
    protected static final String PARAM_ID_NAME = "id";
    public static final String COMMAND_POJO_NAME = "commandPojo";
    public static final String RELATED = "related";

    public abstract CrudControllerConfig getCrudConfig();

    public abstract PojoRegistry getPojoRegistry();

    protected ModelAndView buildModelAndView() {
        ModelAndView v = new ModelAndView();
        v.addObject("gears_restfull_prefix", getCrudConfig().getResfullPrefix());
        return v;
    }

    protected PojoDescriptor loadPojoDescriptor(String pojo) {
        PojoDescriptor pd = pojoDescriptions.get(pojo);
        if (pd == null) {
            try {
                if (factory == null) {
                    factory = new PojoDescriptorFactory(getPojoRegistry());
                }
                pd = factory.buildDescriptorByClass(Class.forName(((PojoRegistryEntry) getPojoRegistry().getRegistry().get(pojo)).getClassName()));
                pojoDescriptions.put(pojo, pd);
            } catch (ConfigurationException e) {
                PrjLogger.debug(ControllerAncestor.class, e.toString());
                if (PrjLogger.isDebugEnabled())
                    e.printStackTrace();

            } catch (ClassNotFoundException e) {
                PrjLogger.debug(ControllerAncestor.class, e.toString());
                if (PrjLogger.isDebugEnabled())
                    e.printStackTrace();
            }
        }
        return pd;
    }

    protected ModelAndView prepareListModel(String pojo, WebRequest request, String viewName, Boolean wrapList, String templateName) {
        ModelAndView v = buildModelAndView();
        final PojoDescriptor descriptor = loadPojoDescriptor(pojo);
        v.setViewName("/" + getCrudConfig().templateFolder + viewName);
        v.addObject("pojoDescriptor", descriptor);
        v.addObject("gears_template_name",templateName) ;
        final DaoService service = getPojoRegistry().getManager(pojo);
        Integer currentPage = 0;
        List outCollection = null;
        if (request.getParameter(ControllerDelegateService.LIST_PAGE_PARAM) != null) {
            currentPage = Integer.parseInt(request.getParameter(ControllerDelegateService.LIST_PAGE_PARAM));
        }
        if (service instanceof ControllerDelegateService) {
            v.addObject("filters", ((ControllerDelegateService) service).getFilters());
            v.addObject("searchable", true);
            final ControllerDelegateService serviceExt = (ControllerDelegateService) service;
            final List list = serviceExt.list(request);


            if (wrapList) {
                outCollection = new NavigableCollection(list, currentPage, descriptor.getItemsPerPage(), serviceExt.count(request));
            } else {
                outCollection = list;
            }


        } else {
            if (wrapList) {
                outCollection = new NavigableCollection(service.list(), currentPage, descriptor.getItemsPerPage());
            } else {
                outCollection = service.list();
            }

        }
        PrjLogger.debug(this, "outCollection " + outCollection.getClass().getCanonicalName() );
        v.addObject("pojoList", outCollection);

        return v;
    }

    protected void initBinderDelegate(WebDataBinder binder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat(getCrudConfig().datePattern);
        dateFormat.setLenient(false);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
        binder.registerCustomEditor(byte[].class, new ByteArrayMultipartFileEditor());

        if (pecache.size() == 0) {
            Iterator i = this.getPojoRegistry().getRegistry().entrySet().iterator();
            final PropertyEditorBuilder pb = new PropertyEditorBuilder();
            PrjLogger.debug(CrudController.class, "NESSUN PROPERTY EDITOR IN CHACHE!  ");
            while (i.hasNext()) {
                final Map.Entry o = (Map.Entry) i.next();
                final PojoRegistryEntry entry = (PojoRegistryEntry) o.getValue();
                PrjLogger.debug(CrudController.class, "Registro propety editor per  " + entry.getClassName());

                try {
                    pecache.put(Class.forName(entry.getClassName()), pb.buildPropertyEditorSupport(loadPojoDescriptor(o.getKey().toString()), entry.getDao()));
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }


            }

        }
        Iterator i = this.pecache.entrySet().iterator();
        while (i.hasNext()) {
            final Map.Entry o = (Map.Entry) i.next();
            final PropertyEditorSupport entry = (PropertyEditorSupport) o.getValue();
            binder.registerCustomEditor((Class) o.getKey(), entry);
        }
    }

    public PojoDescriptorFactory getFactory() {
        if (factory == null) {
            factory = new PojoDescriptorFactory(getPojoRegistry());
        }
        return factory;
    }

    public void setFactory(PojoDescriptorFactory factory) {
        this.factory = factory;
    }
}
